#   Unit tests for classes of pg_copyplot.
#
#   Copyright (C) 2017 Markus Zeindl
#
#   This file is part of pg_copyplot, an adapter for plotting tools 
#   employing postgresql's COPY command.
#
#   This program is free software: you can redistribute it and/or modify
#   it under the terms of the GNU General Public License as published by
#   the Free Software Foundation, either version 3 of the License, or
#   (at your option) any later version.
#
#   This program is distributed in the hope that it will be useful,
#   but WITHOUT ANY WARRANTY; without even the implied warranty of
#   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#   GNU General Public License for more details.
#
#   You should have received a copy of the GNU General Public License
#   along with this program.  If not, see <http://www.gnu.org/licenses/>.

import os
import unittest
import unittest.mock

from pg_copyplot import (Commandline, ScatterPlot, SimpleGnuplot, ScatterPlotSeries, DataSplitter)


class CommandlineTest(unittest.TestCase):
    def test_defaults(self):
        DEFAULTS = Commandline.get_defaults()
        cmdline = Commandline()
        cmdline.parse([])
        self.assertEqual(DEFAULTS, cmdline)

    def test_xspec(self):
        TEST_XSPEC = 3
        TEST_COMMANDLINE = [ '-x', '{}'.format(TEST_XSPEC) ]
        
        expected = Commandline.get_defaults()
        expected[Commandline.ARG_XSPEC.name] = TEST_XSPEC
        observed = Commandline()
        observed.parse(TEST_COMMANDLINE)
        self.assertEqual(expected, observed)

    def test_yspec(self):
        TEST_YSPEC = 8
        TEST_COMMANDLINE = [ '-y', '{}'.format(TEST_YSPEC) ]
        
        expected = Commandline.get_defaults()
        expected[Commandline.ARG_YSPEC.name] = TEST_YSPEC
        observed = Commandline()
        observed.parse(TEST_COMMANDLINE)
        self.assertEqual(expected, observed)

    def test_yspec(self):
        TEST_DELIM = ';'
        TEST_COMMANDLINE = [ '-d', '{}'.format(TEST_DELIM) ]
        
        expected = Commandline.get_defaults()
        expected[Commandline.ARG_COLUMNDELIM.name] = TEST_DELIM
        observed = Commandline()
        observed.parse(TEST_COMMANDLINE)
        self.assertEqual(expected, observed)

class SimpleGnuplotTest(unittest.TestCase):
    def test_construction(self):
        gpi = SimpleGnuplot()
        expected_program = ""
        self.assertEqual(expected_program, gpi.get_complete_program())

    
    def test_add_statement(self):
        gnuplot_instance = SimpleGnuplot()

        expected_initial_program = ""
        self.assertEqual(expected_initial_program, gnuplot_instance.get_complete_program())

        expected_program = "set terminal x11"
        gnuplot_instance.add_statement(expected_program)
        self.assertEqual(expected_program, gnuplot_instance.get_complete_program())

        TEST_INVALID_STATEMENT = "invalid_statement"
        expected_program = expected_program + "\n" + TEST_INVALID_STATEMENT
        gnuplot_instance.add_statement(TEST_INVALID_STATEMENT)
        self.assertEqual(expected_program, gnuplot_instance.get_complete_program())
        
    def test_set_variable(self):
        gpi = SimpleGnuplot()
        expected_program = "set datafile missing \"N/A\""
        gpi.set_variable("datafile missing", "N/A")
        self.assertEqual(expected_program, gpi.get_complete_program())

    def test_add_inlinedata_and_get_datafilehandle(self):
        TEST_ID = "thisdata"
        TEST_DATA = "1,2,3\n3,2,1\n"

        expected_dfh = "$thisdata"
        gpi = SimpleGnuplot()
        dfh = gpi.add_inlinedata_and_get_datafilehandle(TEST_ID, TEST_DATA)
        self.assertEqual(expected_dfh, dfh)

        expected_program = expected_dfh + " <<EOF\n1,2,3\n3,2,1\n\nEOF"
        self.assertEqual(expected_program, gpi.get_complete_program())
        
class ScatterPlotTest(unittest.TestCase):
    def test_plot(self):
        TEST_DATAFILE = "test.dat"
        TEST_DELIM = ";"
        TEST_X = 1
        TEST_Y = 2
        expected_statement = "plot 'test.dat' using 1:2 with linespoints"
        gnuplot_instance_mock = unittest.mock.create_autospec(SimpleGnuplot, spec_set=True)
        plot_instance = ScatterPlot(gnuplot_instance_mock)
        plot_instance.plot(TEST_DATAFILE, TEST_DELIM, TEST_X, TEST_Y)
        gnuplot_instance_mock.add_statement.assert_called_once_with(expected_statement)
        gnuplot_instance_mock.execute.assert_called_once_with()

    def test_plot_multiple_series(self):
        TEST_SERIES = [ ScatterPlotSeries(datafile="test1.dat", delimiter=";", index_x=1, index_y=2),
                        ScatterPlotSeries(datafile="test2.dat", delimiter=";", index_x=4, index_y=9) ]
        expected_statement = "plot 'test1.dat' using 1:2 with linespoints, 'test2.dat' using 4:9 with linespoints"
        gnuplot_instance_mock = unittest.mock.create_autospec(SimpleGnuplot, spec_set=True)
        plot_instance = ScatterPlot(gnuplot_instance_mock)
        plot_instance.plot_multiple_series(TEST_SERIES)
        gnuplot_instance_mock.add_statement.assert_called_once_with(expected_statement)
        gnuplot_instance_mock.execute.assert_called_once_with()


class DataSplitterTest(unittest.TestCase):
    def test_split_by_factor(self):
        TEST_DATA = """a,123
b,321
b,999
a,222
a,111
"""
        TEST_COLUMN_DELIMITER = ","
        TEST_FACTOR_INDEX = 0
        expected = { "a": "a,123\na,222\na,111\n",
                     "b": "b,321\nb,999\n" }
        ds = DataSplitter(TEST_DATA, TEST_COLUMN_DELIMITER)
        observed = ds.split_by_factor(TEST_FACTOR_INDEX)
        self.assertEqual(expected, observed)

    def test_split_by_factor(self):
        TEST_DATA = """1+apfel
5+apfel
4+birne
6+apfel
8+birne
"""
        TEST_COLUMN_DELIMITER = "+"
        TEST_FACTOR_INDEX = 1
        expected = { "apfel": "1+apfel\n5+apfel\n6+apfel\n",
                     "birne": "4+birne\n8+birne\n" }
        ds = DataSplitter(TEST_DATA, TEST_COLUMN_DELIMITER)
        observed = ds.split_by_factor(TEST_FACTOR_INDEX)
        self.assertEqual(expected, observed)
        
        
if __name__ == "__main__":
    unittest.main()
